package epoch.game.core.script.java;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.logging.log4j.Logger;

import epoch.game.core.common.Loggers;
import epoch.game.core.script.java.loader.ScriptLoader;

/**
 * 脚本管理器
 * TODO:will@可以考虑使用spring的ioc来管理
 * 
 * @author will
 * @date 2015年1月25日 下午10:05:05
 *
 */
public class ScriptManager {
	
	private static Logger log = Loggers.SCRIPT_LOGGER;
	
	private static Object lockobj = new Object();
	
	private ConcurrentHashMap<Class<?>, List<? extends IScript>> scripts = new ConcurrentHashMap<Class<?>, List<? extends IScript>>();
	
	private ConcurrentHashMap<Class<?>, List<? extends IScript>> new_scripts = new ConcurrentHashMap<Class<?>, List<? extends IScript>>();
	
	//脚本配置
	private ScriptConfig config;
	//脚本加载器
	private ScriptLoader loader = new ScriptLoader();
	//脚本版本
	private AtomicInteger script_version = new AtomicInteger(1);
	//是否正在加载脚本
	private volatile boolean reloading = false;
	//是否正在切换脚本版本
	private volatile boolean versionChanging = false;
	
	public ScriptManager(){
		
	}
	
	private static ScriptManager me = new ScriptManager();
	
	public static ScriptManager getMe(){
		return me;
	}

	public ScriptConfig getConfig() {
		return config;
	}



	public void setConfig(ScriptConfig config) {
		this.config = config;
	}



	public ScriptLoadResult loadScripts(){
		synchronized (lockobj) {
			if (reloading) {
				ScriptLoadResult loadResult = new ScriptLoadResult();
				loadResult.setResultCode(1);
				loadResult.setErrMsg("正在刷新脚本，请稍后再试！");
				return loadResult;
			}
			reloading = true;			
			ScriptLoadResult loadResult = loader.load(config);
			if (loadResult.getResultCode() == 0) {
				versionChanging = true;
				// unload
				List<? extends IScript> scriptList = scripts.get(IScript.class);
				if (scriptList != null) {
					for (IScript script : scriptList) {
						if (script instanceof BaseScript) {
							((BaseScript) script).unload();
						}
					}
				}
				scripts.clear();
				scripts.putAll(new_scripts);
				new_scripts.clear();
				versionChanging = false;
				script_version.addAndGet(1);
				// init
				scriptList = scripts.get(IScript.class);
				if (scriptList != null) {
					for (IScript script : scriptList) {
						if (script instanceof BaseScript) {
							((BaseScript) script).init();
						}
					}
				}
			} else {
				new_scripts.clear();
			}
			reloading = false;
			return loadResult;
		}
	}

	
	/**
	 * 注册脚本
	 * 
	 * @param script
	 */
	@SuppressWarnings("unchecked")
	public void registerScript(IScript script){
		if(!new_scripts.containsKey(IScript.class)){
			new_scripts.put(IScript.class, new ArrayList<IScript>());
		}
        if(new_scripts.get(IScript.class).contains(script)){
            throw new RuntimeException("register failed:this script has resigtered!");
        }
        ((ArrayList<IScript>)new_scripts.get(IScript.class)).add(script);
        
		Class<?>[] interfaces = script.getClass().getInterfaces();
		for(Class<?> clasz : interfaces){
			if(clasz.equals(IScript.class)){
				continue;
			}
			if(!new_scripts.containsKey(clasz)){
				new_scripts.put(clasz, new ArrayList<IScript>());
			}
            if(new_scripts.get(clasz).contains(script)){
                throw new RuntimeException("register failed:this script has resigtered!");
            }
            ((ArrayList<IScript>)new_scripts.get(clasz)).add(script);
		}
	}
	
	/**
	 * 获取首个实现类
	 * @param clasz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T extends IScript> T getFirstScript(Class<T> clasz){
		if(versionChanging){
			return null;
		}
		if(!scripts.containsKey(clasz)){
			log.error("script not registered!"+clasz);
			return null;
		}
		List<?> list = scripts.get(clasz);
		if(list==null || list.isEmpty()){
			log.error("script not registered!"+clasz);
			return null;
		}
		return (T)list.get(0);
	}
	
	/**
	 * 获取多个实现类
	 * 
	 * @param clasz
	 * @return
	 */
	public <T extends IScript> List<T> getScripts(Class<T> clasz){
		if(versionChanging){
			return new ArrayList<T>();
		}
		if(!scripts.containsKey(clasz)){
			log.error("script not registered!"+clasz);
			return new ArrayList<T>();
		}
		@SuppressWarnings("unchecked")
		List<T> list = (List<T>) scripts.get(clasz);
		return list;
	}
}
